#include "clientList.h"
#include "../logger/logger.h"
#include <unistd.h> /*needed for getpid*/
#include <sys/types.h> /*needed for getpid*/
#include <stdlib.h> /* needed for exit,malloc,free */
#include <stdio.h> /* needed for perror */
#include <errno.h> /* needed for perror */
#include <sys/shm.h> /* needed for shmget,shmat,shmdt,shmctl */
#include <string.h> /* needed for memcpy */
#include <sys/wait.h> /* needed for waitpid */

#define KEY "/tmp/tftp_proj"

//contain a client pid and a client connection socket
//clientPid also serve as a SemiSemaphore
typedef struct
{
	int clientPid;
	int clientSocket;
} client_t, *client_p;

int shmid; //shared memory id
client_p shmList; //shared memory allocation pointer
key_t shmkey; //shared memory key

/**
 * author: Assaf Paz.
 * 
 * addClient
 * 
 * pid - client process ID to add to the container, pid serve as a key
 * sockfd - the stream that links between the client and server
 * 
 * add a client to the container
 * 
 * returns TRUE on success, FALSE otherwise
 */
BOOL addClient(int pid, int sockfd)
{	
	shmkey = ftok(KEY,42);
	
	/*
     * Create the segment.
     */
    if ((shmid = shmget(shmkey, getMaxClients()*sizeof(client_t), SHM_R | SHM_W)) < 0) 
    {
        perror("addClient, get shared memory id");
        exit(1);
    }

    /*
     * Now we attach the segment to our data space.
     */
    if ((shmList = shmat(shmid, NULL, 0)) == (client_p) -1) 
    {
        perror("addClient, attach shared memory");
        exit(1);
    }
	    
	int i;
	for (i = 0; i< getMaxClients(); i++)
	{
		if (shmList[i].clientPid == 0)
		{
			shmList[i].clientPid = pid;
			shmList[i].clientSocket = sockfd;
			logMessage("Client slot <%d> is now occupied with <%d>\n",i,pid);
			break;
		}
	}
	
	if (i==getMaxClients())
	{
		logMessage("addClient, Too many connections\n"); 
		return FALSE;
	}

    logMessage("addClient, account was added succesfully at slot <%d>\n",i);
    return TRUE;
}

/**
 * author: Assaf Paz.
 * 
 * removeClient
 * 
 * pid - client process ID to add to the container
 * 
 * remove the client with the given pid from the container
 * and close the stream to the client
 */
void removeClient(int pid)
{	    
	int i;
	for (i = 0; i< getMaxClients(); i++)
	{
		if (shmList[i].clientPid == pid)
		{
			shmList[i].clientPid = 0;
			int sock = shmList[i].clientSocket;
			close(sock);
			shmList[i].clientSocket = 0;
			break;
		}
	}
	if(shmdt(shmList) == -1) 
	{
    	perror("removeClient, detach shared memory");
    }
	logMessage("removeClient, account was detached succesfully\n");
}

/**
 * author: Assaf Paz.
 * 
 * clearClientList
 * 
 * clear the client container and close all of the streams to the clients
 */
void clearClientList()
{	
	int i;
	for (i=0; i< getMaxClients(); i++)
	{
		int pid = shmList[i].clientPid;
		shmList[i].clientPid = 0;
		kill(pid, SIGTERM);
		close(shmList[i].clientSocket);
		shmList[i].clientSocket = 0;
		waitpid(-1,NULL,0);
	}

	if(shmdt(shmList) == -1) 
	{
    	perror("clearClientList, detach shared memory");
    }
    
    if (shmctl(shmid, IPC_RMID, NULL) == -1) 
	{
    	perror("clearClientList, free shared memory allocation");
    	exit(1);
   	}
   	logMessage("Database was cleared successfully\n");
}

/**
 * author: Assaf Paz.
 * 
 * initializeClientList
 * 
 * initilize the clientList to empty values
 */
void initializeClientList()
{
	shmkey = ftok(KEY,42);
	
	/*
     * Create the segment.
     */
    if ((shmid = shmget(shmkey, getMaxClients()*sizeof(client_t), IPC_CREAT | SHM_R | SHM_W)) < 0) 
    {
        perror("initializeClientList, shared memory allocation");
        exit(1);
    }

    /*
     * Now we attach the segment to our data space.
     */
    if ((shmList = shmat(shmid, NULL, 0)) == (client_p) -1) 
    {
        perror("initializeClientList, attach shared memory");
        exit(1);
    }
    logMessage("Database was initiated successfully\n");
}
